package com.liziba.es.util;

import cn.hutool.json.JSONObject;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.document.DocumentField;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;

import java.io.IOException;
import java.util.List;
import java.util.Map;

/**
 * @auther LiZiBa
 * @date 2021/2/25 23:41
 * @description: elasticsearch操作工具类
 **/

@Slf4j
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ElasticsearchUtil {

    @NonNull
    private RestHighLevelClient client;


    /**
     * 添加索引
     * @param name  索引名
     * @return
     */
    public boolean createIndex(String name) {
        CreateIndexRequest createIndexRequest = new CreateIndexRequest(name);
        log.info("判断索引是否存在：{}", name);
        try {
            if (!indexExits(name)) {
                CreateIndexResponse response = client.indices().create(createIndexRequest, RequestOptions.DEFAULT);
                return response.isAcknowledged();
            }
        } catch (IOException e) {
            log.error("添加索引失败{}", e.getMessage());
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 删除索引
     * @param name     索引名称
     * @return
     */
    public boolean deleteIndex(String name) {
        DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest();
        log.info("判断索引是否存在：{}", name);
        try {
            if (indexExits(name)) {
                AcknowledgedResponse delete = client.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
                if (delete.isAcknowledged()) {
                    return true;
                }
            }
        } catch (IOException e) {
            log.error("删除索引失败{}", e.getMessage());
            e.printStackTrace();
        }
        return false;
    }


    /**
     * 判断索引是否存在
     * @param name
     * @return
     */
    public boolean indexExits(String name) {
        GetIndexRequest getIndexRequest = new GetIndexRequest();
        boolean exists = false;
        log.info("判断索引是否存在：{}", name);
        try {
            exists = client.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            log.error("判断索引是否存在异常：{}", e.getMessage());
            e.printStackTrace();
        }
        return exists;
    }

    /**
     * 添加文档记录
     * @param indexName     索引名称
     * @param jsonObject    文档数据
     * @return
     */
    public boolean createDocument(String indexName, JSONObject jsonObject) {
        // 组装数据，创建请求
        IndexRequest request = new IndexRequest(indexName);
        // 设置规则
        request.timeout(TimeValue.timeValueSeconds(3));
        // 数据放入请求对象
        request.source(jsonObject);
        try {
            IndexResponse response = client.index(request, RequestOptions.DEFAULT);
            if (response.status() == RestStatus.OK) {
                return true;
            }
        } catch (IOException e) {
            log.error("添加文档数据失败：{}", e.getMessage());
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 判断文档是否存在
     * @param indexName     索引名称
     * @param id            文档id
     * @return
     */
    public boolean documentExists(String indexName, String id) {
        GetRequest request = new GetRequest(indexName, id);
        // 参考博客 https://blog.csdn.net/prestigeding/article/details/83591529
        // 指定需要返回字段的上下文，是storedFields的补充与完善，支持通配符
        request.fetchSourceContext(new FetchSourceContext(false));
        // 显示的指定需要返回的字段，默认会返回_source中所有字段
        request.storedFields("_none_");
        boolean exists = false;

        try {
            exists = client.exists(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            log.error("判断文档是否存在异常：{}", e.getMessage());
            e.printStackTrace();
        }
        return exists;
    }


    /**
     * 根据索引名称和文档id获取文档
     * @param indexName     索引名称
     * @param id            文档id
     * @return
     */
    public Map<String, DocumentField> getDocument(String indexName, String id) {
        GetRequest request = new GetRequest(indexName, id);

        try {
            GetResponse documentFields = client.get(request, RequestOptions.DEFAULT);
            return documentFields.getFields();
        } catch (IOException e) {
            log.error("获取文档失败：{}", e.getMessage());
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 根据文档id更新文档
     * @param indexName     索引名称
     * @param id            文档id
     * @param jsonObject    文档内容
     * @return
     */
    public boolean updateDocument(String indexName, String id, JSONObject jsonObject) {
        UpdateRequest request = new UpdateRequest(indexName, id);
        request.timeout(TimeValue.timeValueSeconds(3));
        request.doc(jsonObject, XContentType.JSON);
        try {
            UpdateResponse response = client.update(request, RequestOptions.DEFAULT);
            if (response.status() == RestStatus.OK) {
                return true;
            }
        } catch (IOException e) {
            log.error("更新文档失败：{}", e.getMessage());
            e.printStackTrace();
        }
        return false;
    }


    /**
     * 根据文档id删除文档
     * @param indexName     索引名称
     * @param id            索引id
     * @return
     */
    public boolean deleteDocument(String indexName, String id) {
        DeleteRequest request = new DeleteRequest(indexName, id);

        try {
            DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
            if (response.status() == RestStatus.OK) {
                return true;
            }
        } catch (IOException e) {
            log.error("删除文档失败：{}", e.getMessage());
            e.printStackTrace();
        }
        return false;
    }


    /**
     * 批量插入文档
     * @param indexName     索引名称
     * @param list          批量文档内容
     * @return
     */
    public boolean bulkDocument(String indexName, List<JSONObject> list) {
        BulkRequest bulkRequest = new BulkRequest(indexName);
        bulkRequest.timeout(TimeValue.timeValueSeconds(5));

        list.forEach(item -> {
            bulkRequest.add(new IndexRequest().source(item, XContentType.JSON));
        });
        try {
            BulkResponse response = client.bulk(bulkRequest, RequestOptions.DEFAULT);
            if (response.status() == RestStatus.OK) {
                return true;
            }
        } catch (IOException e) {
            log.error("批量插入文档失败：{}", e.getMessage());
            e.printStackTrace();
        }
        return false;
    }

}
